package com.dimples.security.config;

import com.dimples.common.constant.StrPool;
import com.dimples.common.enums.ViewEnum;
import com.dimples.common.properties.DimplesProperties;
import com.dimples.security.filter.DimplesImageCodeFilter;
import com.dimples.security.filter.DimplesTokenFilter;
import com.dimples.security.handler.DimplesAuthenticationAccessDeniedHandler;
import com.dimples.security.handler.DimplesAuthenticationEntryPoint;
import com.dimples.security.handler.DimplesAuthenticationFailureHandler;
import com.dimples.security.handler.DimplesAuthenticationSuccessHandler;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @author zhongyj <1126834403@qq.com><br/>
 * @date 2021/6/25
 */
@Slf4j
@RequiredArgsConstructor
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class DimplesSecurityConfig extends WebSecurityConfigurerAdapter {

    private final DimplesProperties properties;
    private final DimplesAuthenticationFailureHandler failureHandler;
    private final DimplesImageCodeFilter dimplesImageCodeFilter;
    private final DimplesTokenFilter tokenFilter;
    private final DimplesAuthenticationEntryPoint authenticationEntryPoint;
    private final DimplesAuthenticationAccessDeniedHandler authenticationAccessDeniedHandler;
    @Resource
    private DimplesAuthenticationSuccessHandler successHandler;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.cors();
        http.headers().frameOptions().sameOrigin();

        String[] securityAnonUrl = StrUtil.split(properties.getSecurity().getAnonUrl(), StrPool.COMMA);
        String[] swaggerAnonUrl = StrUtil.split(properties.getSwagger().getAnonUrl(), StrPool.COMMA);

        // 基于token，所以不需要session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 添加验证码过滤器
        http.addFilterBefore(dimplesImageCodeFilter, UsernamePasswordAuthenticationFilter.class);
        http.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);

        // 表单认证配置
        http.formLogin()
                .loginPage(StrPool.SLASH + ViewEnum.LOGIN.getValue())
                .loginProcessingUrl(properties.getCaptcha().getLoginProcessingUrl())
                .successHandler(successHandler)
                .failureHandler(failureHandler)
        ;

        // 认证配置
        http.authorizeRequests()
                // 登录页面允许匿名访问
                .antMatchers(StrPool.SLASH + ViewEnum.LOGIN.getValue()).permitAll()
                // 获取图形验证码允许匿名访问
                .antMatchers(properties.getCaptcha().getCreateUrl()).permitAll()
                // 运行匿名下载用户头像
                .antMatchers(properties.getSecurity().getUserAvatar()).permitAll()
                // 免认证静态资源路径
                .antMatchers(securityAnonUrl).permitAll()
                .antMatchers(swaggerAnonUrl).permitAll()
                .anyRequest().authenticated();

        //权限不足和认证失效处理器
        http.exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(authenticationAccessDeniedHandler);
    }
}
